/**
 * @fileOverview BaseEntity 实体是实际业务数据的载体，包含业务数据模型的元描述
 * @author goodzsq@gmail.com
 * Entity拥有一个数据字典资源的引用，数据字典以外部资源存在
 * Entity拥有一个状态机资源的引用，状态机以作为外部资源存在
 * Entity具有运行状态，在每帧中都会调用update方法驱动状态机的行为
 * Entity之间可以发送和接收消息，Entity收到消息后会交给当前状态机处理
 * Entity是个容器，从而可以构建成树形结构
 *
 * FSM:一个有限状态机包含一个全局的逻辑代码和一个全局的消息处理器和多个状态组成
 * 每个状态由四个函数组成： 进入状态、状态执行、状态退出、消息接收
 * 游戏每次的循环会执行游戏对象当前状态机的全局逻辑代码和当前状态的update
 * 收到消息如果当前执行状态具有消息处理器则进行调用，否则用全局的消息处理器进行调用
 * @example 
 * "我是一个状态机" : {
 *     globalAction:function(){},//全局代码
 *     onMessage:function(){},//全局消息接收
 *     "状态1":{
 *         onEnter:function(){},
 *         onExit:function(){},
 *         onUpdate:function(dt){},
 *         onMessage:function(){}
 *     },
 *     "状态2":{
 *         onEnter:function(){},
 *         onExit:function(){},
 *         onUpdate:function(dt){},
 *         onMessage:function(){}
 *     }
 * }
 */
var Resource = gg.module('Resource');
var Message = gg.module('Message');
var noop = gg.noop;
var BaseClass = gg.module('BaseObject');

module.exports = gg.createClass({
    attributes: {
        dict: {
            lable: '类别',
            type: 'datatype.res.Dict',
            defaultValue: '',
            getter: function() {
                return this.defaultGetter('dict');
            },
            setter: function(v) {
                this.defaultSetter('dict', v);
                var DictType = gg.module('datatype.res.Dict');
                this.dict = DictType.load(Resource.get('Dict/' + v)) || {};
                this.set('fsm', this.dict['fsm']);
            }
        },
        fsm: {
            label: '状态机',
            type: 'datatype.res.FSM',
            defaultValue: null,
            setter: function(v) {
                this.defaultSetter('fsm', v);
                var FSMType = gg.module('datatype.res.FSM');
                this.fsm = FSMType.load(Resource.get('FSM/' + v)) || {};
                if (typeof this.fsm.globalAction === 'function') {
                    this.globalAction = this.fsm.globalAction;
                } else {
                    this.globalAction = noop;
                }
            }
        },
        state: {
            label: '当前状态',
            type: 'datatype.String',
            defaultValue: '',
            setter: function(v) {
                //保存上一个状态
                this.previousState = this.get('state');
                //保存当前状态
                this.defaultSetter('state', v);
                //1.退出上一个状态
                if (this.currentState && this.currentState.onExit) {
                    this.currentState.onExit.call(this);
                }
                //2.切换当前状态
                this.currentState = this.fsm[v] || {};
                if (this.currentState && typeof this.currentState.onUpdate === 'function') {
                    this.currentAction = this.currentState.onUpdate;
                } else {
                    this.currentAction = noop;
                }
                //3.进入到当前状态
                if (this.currentState && this.currentState.onEnter) {
                    this.currentState.onEnter.call(this);
                }
            }
        }
    },
    methods: {
        fsm: {}, //有限元状态机
        currentState: {}, //当前状态机
        previousState: '', //上一个状态,保存状态机的key字符串类型
        globalAction: noop, //全局状态
        currentAction: noop, //当前执行逻辑
        _dictData: {}, //数据字典
        update: function(dt) {
            if (!this.pause) {
                this.globalAction.call(this, dt);//执行全局状态行为
                this.currentAction.call(this, dt);//执行当前状态行为
            }
            for (var i = 0; i < this.children.length; i++) {
                this.children[i].update(dt);
            }
        },
        /**
         * 字典数据
         * @param {String} field 字段名称
         * @return {Mixed}
         */
        dict: function(field) {
            return this._dictData[field] == null ? '' : this._dictData[field];
        },
        /**
         * 反转到上一个状态
         */
        revertToPerviousState: function() {
            this.set('state', this.previousState);
        },
        /**
         * 向目标实体发送消息
         * @param {entity.Base | String} target 目标对象或者目标对象id
         * @param {String} msg 消息
         * @param {Object} data 消息内容
         * @param {Number} delay 延时毫秒
         * @return {Boolean}
         */
        sendMessage: function(target, msg, data, delay) {
            if (target instanceof BaseEntity) {
                Message.send(this.cid, target.cid, msg, data, delay);
                return true;
            } else if (typeof target === 'string') {
                Message.send(this.cid, target, msg, data, delay);
                return true;
            }
            return false;
        },
        /**
         * 接收到消息
         * @param {String} sender 消息发送者
         * @param {String} msg 消息
         * @param {Object} data 消息数据
         */
        handleMessage: function(sender, msg, data) {
            if (this.currentState && this.currentState.onMessage) {
                this.currentState.onMessage.call(this, sender, msg, data);
                return true;
            } else if (this.fsm && this.fsm.onMessage) {
                this.fsm.onMessage.call(this, sender, msg, data);
            }
            return false;
        },
        /**
         * 暂停
         */
        pause: function() {
            this.pause = true;
        },
        /**
         * 恢复
         */
        resume: function() {
            this.pause = false;
        }
    },
    events: {
    }
}, BaseClass, ['Container']);


/** @fileend */

//unittest
UnitTest.add('BaseEntity',
        function() {
            var obj = gg.object({
                classType: 'BaseEntity',
                dict: 'Character.0001',
                fsm: 'Enemy.001'
            });
            this.test(obj instanceof BaseEntity === true, '对象实例=BaseEntity', '对象实例');
            this.test(obj.get('dict') === 'Character.0001', '字典');
        }
);
